/**
 * ConstraintAbstract
 * ----------------------------------------------
 * 
 * This abstract class represents integrity constraints for XDF, XDFC;
 * 
 * - XDF extends  
 * - XDFC extends
 *  
 * @author caio thomas
 * @version 
 */
package model.constraints;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.StringTokenizer;

import automata.Alphabet;
import automata.Fsa;
import automata.Label;
import automata.State;
import automata.StateSet;
import automata.Transitions;

public abstract class AbstractConstraint implements Serializable {
	
	private static final long serialVersionUID = 1L;
	protected String nameConstraint; 
	protected Fsa contextFsa;
	protected String context;
	protected Fsa sourceFsa;
	protected String leftPath;
	protected String leftValue;
	protected Fsa targetFsa;
	protected String rightPath;
	protected int rightValue;
	protected int npaths = 1;//path defines how the FSA has
	protected Hashtable<Fsa, Hashtable<State, String>> equality = new Hashtable<Fsa, Hashtable<State, String>>();
	protected ArrayList<Integer> j = new ArrayList<Integer>();
	
	public String getNameConstraint() {
		return nameConstraint;
	}

	public void setNameConstraint(String nameConstraint) {
		this.nameConstraint = nameConstraint;
	}

	public Fsa getContextFsa() {
		return contextFsa;
	}

	public void setContextFsa(Fsa contextFsa) {
		this.contextFsa = contextFsa;
	}

	public String getContext() {
		return context;
	}

	public void setContext(String context) {
		this.context = context;
	}

	public Fsa getSourceFsa() {
		return sourceFsa;
	}

	public void setSourceFsa(Fsa sourceFsa) {
		this.sourceFsa = sourceFsa;
	}

	public String getLeftPath() {
		return leftPath;
	}

	public void setLeftPath(String leftPath) {
		this.leftPath = leftPath;
	}

	public String getLeftValue() {
		return leftValue;
	}

	public void setLeftValue(String leftValue) {
		this.leftValue = leftValue;
	}

	public Fsa getTargetFsa() {
		return targetFsa;
	}

	public void setTargetFsa(Fsa targetFsa) {
		this.targetFsa = targetFsa;
	}

	public String getRightPath() {
		return rightPath;
	}

	public void setRightPath(String rightPath) {
		this.rightPath = rightPath;
	}

	public int getRightValue() {
		return rightValue;
	}

	public void setRightValue(int rightValue) {
		this.rightValue = rightValue;
	}

	public int getNpaths() {
		return npaths;
	}

	public void setNpaths(int npaths) {
		this.npaths = npaths;
	}

	public Hashtable<Fsa, Hashtable<State, String>> getEquality() {
		return equality;
	}

	public void setEquality(Hashtable<Fsa, Hashtable<State, String>> equality) {
		this.equality = equality;
	}

	public ArrayList<Integer> getJ() {
		return j;
	}

	public void setJ(ArrayList<Integer> j) {
		this.j = j;
	}
	
	public int getNPaths() {
		return npaths;
	}	
	
	public void setJ(){
		Iterator<State> i = sourceFsa.get_final_states().iterator();
		
		while (i.hasNext()) {
			j.add(i.next().elt);
		}
	}
	
	public int getJ(Integer state){
		return j.indexOf(state);
	}
	
	public String getEquality(Fsa automata, State node){
		Hashtable<State, String> h;
		String type = null;
		
		if(equality.containsKey(automata)) {
			h = equality.get(automata);
			type = h.get(node);
		}
		
		return type;
	}
	

	public void setRightValue(String rightPath) {
		if(rightPath.equals("value"))
			this.rightValue = 0;
		else
			this.rightValue = 1;
	}
		
	/**
	 * This function define type {V,N} and FSA (source or target)
	 * @param t
	 * @param automata left (l) or right (r)
	 * @return void
	 */
	public void setEquality(String t, String automata){
		
		if(automata.equals("l"))//get values String and save in variable  
				leftValue = t;
		
		String [] types = t.split(",");
		Fsa f = null;
		
		if(automata.equals("l"))
			f = sourceFsa;
		if(automata.equals("r"))
			f = targetFsa;
		
		StateSet ss = f.get_final_states();
		int j = 0;
		Iterator<State> i = ss.iterator();
		Hashtable<State, String> h = new Hashtable<State, String>();
		
		while (i.hasNext() || j<types.length){
			h.put(i.next(), types[j]);
			j++;
		}			
		
		equality.put(f, h);
	}
	
	/**
	 * Function that creates FSA (automaton) from a path
	 * @param path
	 * @return new FSA
	 */
	public Fsa pathToFsa(String path){
		//System.out.println("Path To FSA: " + path);

		Fsa f = new Fsa();
		int i = 1;

		Label star = new Label("*");
		
		State start = new State(1);
		State sant;
		State s;
		
		Alphabet alpha = new Alphabet();
		StateSet ss = new StateSet();
		StateSet fs = new StateSet();
		
		Transitions t = new Transitions();
		String token = new String();

		ss.add(start);
		sant = start;
		
		StringTokenizer st = new StringTokenizer(path, " ");
		
		while (st.hasMoreTokens()){
			token = st.nextToken();
			
			if (token.equals("|")){
				fs.add(sant);
				sant = start;
			} else if (token.equals("//")){
				alpha.add(star);
				StateSet ss2 = new StateSet();
				ss2.add(sant);
				t.put(sant, star, ss2);
			} else { 

			//if ((!token.equals("/")) && (!token.equals("//")) && (!token.equals("|"))){
				
				String sliptExp[] = token.split("/");
				
				for (String item: sliptExp){
					System.out.println("item: " + item + "\n");
					if(!item.equals("")){
						Label l = new Label(item);
					
						if(alpha.contains(l)){
							System.out.println("ligacao de estado " + sant.elt + 1);
							sant = new State (sant.elt+1);
						} else {
							i++;
							alpha.add(l);
							s = new State(i);
							ss.add(s);
							StateSet ss1 = new StateSet();
							ss1.add(s);
							t.put(sant, l, ss1);
							sant = s;
						}
					}
				}
			}
		}
		
		fs.add(sant);
		f.set_alphabet(alpha);
		f.set_states(ss);
		f.set_start_state(start);
		f.set_final_states(fs);
		f.set_transitions(t);
		
		return f;
	}
	
	/**
	 * Function that creates a path from the context FSA
	 * @param s
	 * @return void, but set new context
	 */
	public void setContextFsa(String s) {
		context = s;		
		contextFsa = pathToFsa(s);
	}
	
	/**
	 * Function that creates a path from the targetFsta
	 * @param s
	 * @return void, but set new targetFsa
	 */
	public void setTargetFsa(String s){
		rightPath = s;
		targetFsa = pathToFsa(s);
	}	

	
	public abstract int getFsaPosition(Fsa f);
	public abstract String toString();
	public abstract String toStringPath();	
}
